'''
    course_manager.py
      
        The course_manager module contains only one class called
        'manager'. This class is used to manage the courses in the
        student's graduation plan

        class manager
      
            The 'manager' class manages all the taken and in progress courses 
            a student has added to their plan. It makes use of the course
            class for an array of the said courses and some of its methods
            such as change_grade().
'''

import course

NUM_SEMESTERS = 4

'''
    manager

    The 'manager' class manages all the taken and in progress courses 
    a student has added to their plan. It makes use of the course
    class for an array of the said courses and some of its methods
    such as change_grade().
'''
class manager:
    
    '''
        __init__()

        Initializes a course manager with an
        empty course array
    '''
    def __init__(self):
        self._courses = []

    '''
        has_taken_by()

        **description here**
    '''
    def has_taken_by(self, req, semester):
        class_found = False
        # sometimes in these lists there will be an empty entry
        if len(req) <= 4:
            return True
        for course in self._courses:
            if course.get_id().split('\t')[0] == req.split('\t')[0]:
               if course.get_semester() < semester:
                   class_found = True 
        return class_found
        
    '''
        can_add()

        Checks if a course can be added to the picked semester
    '''
    def can_add(self, new_course, semester):
        sem_creds = self.credit_total_semester(semester)
        sem_creds += new_course.get_credits()
        if sem_creds > 20:
            return False
        for req in new_course.get_prerequisites():
            
            satisfied = False
            if type(req) is list:
               one_found = False
               for prereq in req:
                 if self.has_taken_by(prereq, semester):
                    one_found = True
               satisfied = one_found
            else:

               satisfied = self.has_taken_by(req, semester)
            if not satisfied:
               return False
        #make sure the course is not in the current semester
        if semester in self.semesters_taken(new_course):
            return False
        #make sure the course has been taken no more than 3 times
        num_times_taken = len(self.semesters_taken(new_course))
        if num_times_taken > 2:
            return False       
        #make sure all prereqs are there 
        return True



    '''
        can_still_add()

        Checks if a course still stands added after another is removed
    '''
    def can_still_add(self, new_course, semester):
        for req in new_course.get_prerequisites():
            satisfied = False
            if type(req) is list:
               one_found = False
               for prereq in req:
                 if self.has_taken_by(prereq, semester):
                    one_found = True
               satisfied = one_found
            else:

               satisfied = self.has_taken_by(req, semester)
            if not satisfied:
               return False
        return True
    
    '''
        semesters_taken()

        Returns the number of times a course has been taken
        so far
    ''' 
    def semesters_taken(self, new_course):
        semesters = []
        for course in self._courses:
            if course.get_name() == new_course.get_name():
                semesters.append(course.get_semester())
        return semesters
             
    '''
        add()

        Adds a course to the picked semester
    '''    
    def add(self, new_course, semester):
        self._courses.append(course.student_course(new_course, semester, 'N/A'))
        
    '''
        update_grade()

        Updates a course grade
    ''' 
    def update_grade(self, name, semester, grade):
        for course in self._courses:
            if course.get_semester() == semester and course.get_name() == name:
                course.change_grade(grade)
      
    '''
        remove_effect()

        Returns courses that need to be removed from
        the plan due to requirements failure
    '''          
    def remove_effect(self, course_name, semester, dependants):
        courses_to_remove= []
        for course in self._courses:
            if course.get_name() in dependants:
                courses_to_remove.append(course.get_name())
        return courses_to_remove
    
    '''
        remove()

        Removes a course from the graduation plan
    '''
    def remove(self, course_name, semester):
            for course in self._courses:
                if course.is_named(course_name) and course.get_semester() == semester:
                    self._courses.remove(course)
    
    '''
        all_taken()

        Returns all courses the student has or is taking
    '''
    def all_taken(self):
        return self._courses

    '''
        has_taken()

        Checks if a course has been taken already
    '''
    def has_taken(self, new_course):
        
        for course in self._courses:
            if course.get_name() == new_course.get_name():
                return True 
        return False
    
    '''
        courses_being_taken()

        Returns all courses that are being taken in a
        particular semester
    '''
    def courses_being_taken(self, semester):
        current = []
        for course in self._courses:
            if course.get_semester() == semester:
                current.append(course)
        return current

    '''
        credit_total_semester()

        Returns the sum of all the credits of the courses being taken in a given
        semester.
    
    '''
    def credit_total_semester(self, semester):
        cur_courses = self.courses_being_taken(semester)
        sum = 0
        for i in range(0, len(cur_courses)):
            sum += cur_courses[i].get_credits()
        return sum
    
    
    '''
        get_valid_semesters()

        Returns valid semesters of the year
    '''
    def get_valid_semesters(self, course, year):
        valid_semesters = []
        for i in range(NUM_SEMESTERS):
            if self.can_add(course, year * NUM_SEMESTERS + i):
                valid_semesters.append(i)
        
        return valid_semesters


    '''
    can_take()

    returns true if the course can be taken in a given 
    semester, False otherwise

    '''
    def can_take(self, course, semester):
        return semester % 4 in self.get_valid_semesters(course, \
			semester / 4)

    '''
        change_grade()

        Changes the course grade
    '''
    def change_grade(self, new_course, semester, grade):
        course = self.get_course(new_course, semester)
        course.set_grade(grade)
                
    '''
        get_course()

        Returns a new course
    '''
    def get_course(self, new_course, semester):
        for course in self._courses:
            if course.is_named(new_course) and course.get_semester() == semester:
                return course
        return 0
    

    '''
        can_remove()

        Checks if a course can be removed
    '''
    def can_remove(self, old_course, semester):
        self.remove(old_course.get_id(), semester)
        for course in self._courses:
            if self.can_still_add(course, course.get_semester()):
               pass
            else:
                self.add_std(old_course, semester)
                return False
        self.add_std(old_course, semester)
        return True

    def add_std(self, nc, sem):
        self._courses.append(course.student_course(nc._course, sem, 'N/A'))
